<?php
// $Id: Loader.php 27 2010-11-16 12:49:54Z mjohnson60@gmail.com $


require_once (ROOT . '/libraries/File.php');
require_once (ROOT . '/libraries/Folder.php');

/**
 * Enter description here ...
 * 
 * @author Mark Phillip Johnson
 * @version 1.0
 * @copyright Copyright (c) 2010, markjohnson
 * @license http://www.gnu.org/licenses/gpl.html The GNU General Public License v3.0
 *
 */
class Loader extends Object
{
	/**
	 * Enter description here ...
	 * @var Loader
	 */
	private static $instance = NULL;
	
	/**
	 * Enter description here ...
	 * @var Folder
	 */
	private static $root;
	
	/**
	 * @var array Enter description here ...
	 */
	private static $includes = NULL;
	
	/**
	 * @var array Enter description here ...
	 */
	private static $modules = array();
	
	/**
	 * @var array Enter description here ...
	 */
	private static $modulePaths = array();
	
	/**
	 * @var array Enter description here ...
	 */
	private static $libraries = array();
	
	/**
	 * Enter description here ...
	 * @var mixed
	 */
	private static $loaded = array();
	
	/**
	 * Enter description here ...
	 * @var mixed
	 */
	protected static $listing = array(
		'class' => array(
			'extension' => '.php', 
			'core' => TRUE
		), 
		'inc' => array(
			'extension' => '.inc', 
			'core' => TRUE
		)
	);

	/**
	 * Enter description here ...
	 */
	public function __construct()
	{
		spl_autoload_register(array(
			__CLASS__, 
			'autoload'
		));
		
		self::$instance = & $this;
		
		if (empty(self::$root))
		{
			self::reloadDirectories();
		}
	}

	/**
	 * Enter description here ...
	 * @return Ambiguous
	 */
	public static function &getInstance()
	{
		if (!isset(self::$instance))
		{
			new Loader();
		}
		
		return self::$instance;
	}

	/**
	 * Enter description here ...
	 */
	public static function reloadDirectories()
	{
		if (empty(self::$root))
		{
			self::$root = new Folder();
		}
		
		if (empty(self::$libraries))
		{
			$dir = self::$root->subdirectory('libraries');
			
			if ($dir !== FALSE)
			{
				self::$libraries[] = $dir;
			}
			
			Loader::import('Config');
		}
		
		if (empty(self::$includes))
		{
			$dir = self::$root->subdirectory('includes');
			
			if ($dir !== FALSE)
			{
				self::$includes[] = $dir;
			}
		}
		
		if (empty(self::$modules))
		{
			$dir = self::$root->subdirectory('modules');
			
			if ($dir !== FALSE)
			{
				self::$modules[] = $dir;
			}
			
			$dir = self::$root->subdirectory(Config::confPath() . '/modules');
			
			if ($dir !== FALSE)
			{
				self::$modules[] = $dir;
			}
			
			foreach (self::$modules as $folder)
			{
				self::$modulePaths[] = $folder->pwd();
			}
		}
	}

	/**
	 * Enter description here ...
	 * @param mixed $type (lib|helper|theme|module|installer|inc)
	 * @param unknown_type $name
	 * @param unknown_type $search
	 * @param unknown_type $file
	 * @return boolean
	 */
	public static function import($type = NULL, $name = NULL, $search = array(), $file = NULL)
	{
		$_this = & Loader::getInstance();
		
		if (empty($type))
		{
			return FALSE;
		}
		
		if (is_array($type))
		{
			extract($type, EXTR_OVERWRITE);
		}
		
		if (!is_array($name) && !empty(self::$loaded[$name]))
		{
			return TRUE;
		}
		else if (is_array($name))
		{
			$retVal = TRUE;
			foreach ($name as $aName)
			{
				if (!empty(self::$loaded[$aName]) || class_exists($aName, FALSE))
				{
					$retVal = ($retVal && TRUE);
				}
				else
				{
					$retVal = FALSE;
				}
			}
			
			if ($retVal === TRUE)
			{
				return $retVal;
			}
		}
		else if (!empty($name))
		{
			if (class_exists($name, FALSE))
			{
				return TRUE;
			}
			
			$name = array(
				$name
			);
		}
		
		if (!empty($file))
		{
			if (file_exists('./' . $file))
			{
				return require_once ('./' . $file);
			}
			else if (file_exists($file))
			{
				return require_once ($file);
			}
			
			return FALSE;
		}
		
		if (empty($type) || is_array($type))
		{
			$type = 'lib';
			if (empty($search))
			{
				$search[] = 'libraries';
			}
		}
		
		if ($name === NULL && $file === NULL)
		{
			$name = array(
				$type
			);
			$ext = 'php';
		}
		
		$directories = array();
		$excluded = array();
		
		$ext = 'php';
		switch ($type)
		{
			case 'module':
				$ext = 'module';
				
				$directories = self::$modules;
				$excluded = self::$modulePaths;
				
				break;
			case 'installer':
				$ext = 'install';
				
				$directories = self::$modules;
				$excluded = self::$modulePaths;
				
				break;
			case 'inc':
				$ext = 'inc';
				
				$directories = self::$modules;
				$directories = array_merge($directories, self::$includes);
				
				$excluded = self::$modulePaths;
				$excluded[] = 'includes';
				
				break;
			default:
				$directories[] = self::$root;
				
				break;
		}
		
		if (!empty($search))
		{
			foreach ($search as $path)
			{
				if (!in_array($path, $excluded))
				{
					$result = self::$root->subdirectory($path);
					
					if ($result !== FALSE)
					{
						$directories[] = $result;
					}
				}
			}
		}
		
		return Loader::findFilesInFolders($name, $ext, $directories);
	}

	/**
	 * Enter description here ...
	 * 
	 * @param array $files
	 * @param array $folders
	 * @return boolean
	 */
	private static function findFilesInFolders($files, $ext, $folders)
	{
		$retVal = FALSE;
		
		foreach ($files as $file)
		{
			foreach ($folders as $folder)
			{
				$paths = $folder->findRecursive($file . '\\.' . $ext);
				
				if (!empty($paths))
				{
					foreach ($paths as $path)
					{
						$retVal = require_once ($path);
						self::$loaded[$file] = $retVal;
					}
				}
			}
		}
		
		return $retVal;
	}

	/**
	 * Enter description here ...
	 * @param unknown_type $class
	 * @return boolean
	 */
	public static function autoload($class)
	{
		if (class_exists($class, FALSE) || interface_exists($class, FALSE))
		{
			return TRUE;
		}
		
		$_this = & Loader::getInstance();
		
		if (isset($_this->classes[$class]))
		{
			try
			{
				require_once ($_this->classes[$class]);
			}
			catch (HrException $e)
			{
				print(HrException::createFromException($e)->getTraceAsString());
			}
			catch (Exception $e)
			{
				print($e->getTraceAsString());
			}
			
			return TRUE;
		}
		else
		{
			$files = self::$root->findRecursive($class . '\.(module|php)');
			if (count($files) > 0)
			{
				foreach ($files as $file)
				{
					try
					{
						require_once ($file);
					}
					catch (HrException $e)
					{
						print(HrException::createFromException($e)->getTraceAsString());
					}
					catch (Exception $e)
					{
						print($e->getTraceAsString());
					}
				}
			}
		}
	}
}
